LOCAL SourceWidth:word,SourceHeight:word,SourceOffset:word,SourceWidth2:word,SourceHeight2:word,DestWidth2:word,DestHeight2:word,DecisionX:word,DecisionY:word,ClippedWidth:word,ClippedHeight:word,ByteWidth:word,Plane:byte=LocalStk
push bp
mov bp, sp
sub sp, LocalStk ; Create space for local variables
push si
push di
push ds
push ds
lds si, [Bitmap] ; Point DS:SI to bitmap data
lodsb
xor ah, ah ; Clear ah
mov SourceWidth, ax ; Get bitmap width
lodsb
mov SourceHeight, ax ; Get bitmap height
pop ds
shl [_LeftClip], 2 ; This clipping code assumes 1 pixel
shl [_RightClip], 2 ; granularity
cmp [DestWidth], 2 ; If destination width is less than 2
jl Done ; then don't draw it.
cmp [DestHeight], 2 ; If destination height is less than 2
jl Done ; then don't draw it.
mov ax, [DestY] ; If it is completely below the
cmp ax, [_BottomClip] ; lower clip bondry,
jg Done ; then don't draw it.
add ax, [DestHeight] ; If it is above clip boundries
dec ax ; then don't draw it.
cmp ax, [_TopClip]
jl Done
mov ax, [DestX] ; If it is to the right of the
cmp ax, [_RightClip] ; then don't draw it.
jg Done
add ax, [DestWidth] ; If it is completely to the left
dec ax ; of the left clip boundry,
cmp ax, [_LeftClip] ; then don't draw it.
jl Done
mov ax, [DestWidth] ; ClippedWidth is initially set to
mov [ClippedWidth], ax ; the requested dest width.
shl ax, 1 ; Initialize the X decision var
neg ax ; to be -2*DestHeight
mov [DecisionX], ax ;
mov ax, [DestHeight] ; ClippedHeight is initially set to
mov [ClippedHeight], ax ; the requested dest size.
shl ax, 1 ; Initialize the Y decision var
neg ax ; to be -2*DestHeight
mov [DecisionY], ax ;
mov [SourceOffset], 0 ; Offset into source bitmap
movsx eax, [_TopClip] ; If Y is below the top
mov edx, eax ; clipping boundry, then we don't
sub dx, [DestY] ; need to clip the top, so we can
js NoTopClip ; jump over the clipping stuff.
mov [DestY], ax ; This block performs clipping on the
sub [ClippedHeight], dx ; top of the bitmap. I have
movsx ecx, [SourceHeight] ; optimized this block to use only 4
imul ecx, edx ; 32-bit registers, so I'm not even
mov eax, ecx ; gonna try to explain what it's doing.
mov edx, 0 ; But I can tell you what results from
movsx ebx, [DestHeight] ; this: The DecisionY var is updated
idiv ebx ; to start at the right clipped row.
movsx edx, [SourceWidth] ; Y is moved to the top clip
imul edx, eax ; boundry. ClippedHeight is lowered since
add [SourceOffset], dx ; we won't be drawing all the requested
imul eax, ebx ; rows. SourceOffset is incremented by
sub ecx, eax ; the number of pixels clipped off the top.
sub ecx, ebx ;
shl ecx, 1 ;
mov [DecisionY], cx ; <end of top clipping block >
NoTopClip:
mov ax, [DestY] ; If the bitmap doesn't extend over the
add ax, [ClippedHeight] ; bottom clipping boundry, then we
dec ax ; don't need to clip the bottom, so we
cmp ax, [_BottomClip] ; can jump over the bottom clip code.
jle NoBottomClip ;
mov ax, [_BottomClip] ; Clip off the bottom by reducing the
sub ax, [DestY] ; ClippedHeight so that the bitmap won't
inc ax ; extend over the lower clipping
mov [ClippedHeight], ax ; boundry.
NoBottomClip:
movsx eax, [_LeftClip] ; If X is to the left of the
mov edx, eax ; top clipping boundry, then we don't
sub dx, [DestX] ; need to clip the left, so we can
js NoLeftClip ; jump over the clipping stuff.
mov [DestX], ax ; This block performs clipping on the
sub [ClippedWidth], dx ; left of the bitmap. I have
movsx ecx, [SourceWidth] ; optimized this block to use only 4
imul ecx, edx ; 32-bit registers, so I'm not even
mov eax, ecx ; gonna try to explain what it's doing.
mov edx, 0 ; But I can tell you what results from
movsx ebx, [DestWidth] ; this: The DecisionX var is updated
idiv ebx ; to start at the right clipped column.
add [SourceOffset], ax ; X is moved to the left clip
imul eax, ebx ; boundry. ClippedWidth is reduced since
sub ecx, eax ; we won't be drawing all the requested
sub ecx, ebx ; cols. SourceOffset is incremented by
shl ecx, 1 ; the number of pixels clipped off the left.
mov [DecisionX], cx ; <end of left clipping block >
NoLeftClip:
mov ax, [DestX] ; If the bitmap doesn't extend over the
add ax, [ClippedWidth] ; right clipping boundry, then we
dec ax ; don't need to clip the right, so we
cmp ax, [_RightClip] ; can jump over the right clip code.
jle NoClipRight ;
mov ax, [_RightClip] ; Clip off the right by reducing the
sub ax, [DestX] ; ClippedWidth so that the bitmap won't
inc ax ; extend over the right clipping
mov [ClippedWidth], ax ; boundry.
NoClipRight:
;Multiply all the widths by 2 to reduce inner loop calculations.
mov ax, [SourceWidth]
shl ax, 1
mov [SourceWidth2], ax
mov ax, [SourceHeight]
shl ax, 1
mov [SourceHeight2], ax
mov ax, [DestWidth]
shl ax, 1
mov [DestWidth2], ax
mov ax, [DestHeight]
shl ax, 1
mov [DestHeight2], ax
mov ax, [_ScrnLogicalByteWidth]
mov [ByteWidth], ax
;Calculate starting video address
mov ax,SCREEN_SEG
mov es,ax
mov ax,[DestY] ; Calculate dest screen row
mov bx,[_ScrnLogicalByteWidth] ; by mult. dest Y coord by Screen
mul bx ; width then adding screen offset
mov di,[ScrnOffs] ; store result in DI
add di,ax
mov cx,[DestX] ; Load X coord into CX and make a
mov dx,cx ; copy in DX
shr dx,2 ; Find starting byte in dest row
add di,dx ; add to DI giving screen offset of
; first pixel's byte
lds si, [Bitmap] ; Point DS:SI to bitmap data
add si, 2 ; Skip over width, height
add si, [SourceOffset] ; Skip over clipped off pixels.
mov dx, SC_INDEX ; Point the VGA Sequencer to the Map
mov al, MAP_MASK ; Mask register, so that we only need
out dx, al ; to send out 1 byte per column.
inc dx ; Move to the Sequencer's Data register.
and cx, 3 ; Calculate the starting plane. This is
mov al, 11h ; just:
shl al, cl ; Plane = (11h << (X AND 3))
mov [Plane], al ; Save Plane for later
out dx, al ; Select the first plane.
;------------------ NOT SO CRITICAL OUTER LOOP BEGIN ----------------------
RowLoop:
push si ; Save the starting source index
push di ; Save the starting dest index
mov bx, [DecisionY] ; Make BX be our decision variable
mov cx, [ClippedHeight] ; How many rows to draw
mov dx, [ByteWidth] ; How far to next row
mov al, ds:[si] ; Get the first source pixel
;---------------------- CRITICAL INNER LOOP BEGIN --------------------------
ColumnLoop:
mov es:[di], al ; Draw a pixel
add di, dx ; Go on to the next screen row
dec cx ; Decrement line counter
jz DoneWithCol ; See if we're done with this column
add bx, [SourceHeight2] ; Increment the decision variable
js ColumnLoop ; Draw this source pixel again
IncSourceRow:
add si, [SourceWidth] ; Move to the next source pixel
sub bx, [DestHeight2] ; Decrement the decision variable
jns IncSourceRow ; See if we need to skip another source pixel
mov al, ds:[si] ; Get the next source pixel
jmp ColumnLoop ; Start drawing this pixel
;---------------------- CRITICAL INNER LOOP END --------------------------
DoneWithCol:
pop di ; Restore DI to top row of screen
pop si ; Restore SI to top row of source bits
rol [Plane], 1 ; Move to next plane
adc di, 0 ; Go on to next screen column
mov dx, SC_INDEX ; Tell the VGA what column we're in
inc dx ; Point to data port
mov al, [Plane] ;
out dx, al ; by updating the map mask register
mov ax, [SourceWidth2] ; Increment the X decision variable
add [DecisionX], ax ; Update the X decision variable
js NextCol ; Jump if we're still in the same source col.
IncSourceCol:
inc si ; Move to next source column
mov ax, [DestWidth2]
sub [DecisionX], ax ; Decrement X decision variable
jns IncSourceCol ; See if we skip another source column
NextCol:
dec [ClippedWidth] ; If we're not at last column
jnz RowLoop ; then do another column
;------------------- NOT SO CRITICAL OUTER LOOP END -------------------------
Done:
pop ds ; restore data segment
shr [_LeftClip], 2 ; Put these back like they were!